package kb;

import java.util.HashMap;

import entities.concept.AndConcept;
import entities.concept.Concept;
import entities.concept.DomainConceptEQ;
import entities.role.Role;

/**
 * @serial A {@code DescriptionLogicsKBox} that consists of a TBox with axioms
 *         and an ABox with assertions can be serialized.
 * @author j55wu
 * 
 */
public class DescriptionLogicsKB implements KB {
	private final String name;
	private final TBox tb;
	private final ABox ab;
	private KBStat stat;
	private static final String KB_default_name = "no-name";

	// a KB can have at most one KB stat
	public KBStat getKBStat() {
		if (stat == null) {
			this.stat = new KBStat(this);
		}
		return stat;
	}

	// at present we cache f=k computed by queries, also possible to cache all
	// occurrences in ABox
	// private HashMap<Individual, HashMap<RolePath, HashMap<Role, String>>>
	// cachedDomainConcepts;
	// private static long domainCacheHit = 0;

	// public static long getDomainCacheHit() {
	// return domainCacheHit;
	// }
	//
	// public void setCachedDomainConceptEQEntry(Individual ind, RolePath rp,
	// DomainConceptEQ<?,?> dc)
	// throws CacheNodeException{
	// //if rp is used in other optimization, its concept may not be null
	// Concept filler = rp.getConcept();
	// RolePath myrp;
	// myrp = new RolePath(null, rp.getOffset());
	// myrp.getRoles().addAll(rp.getRoles());
	// if(filler != null){
	// //get a new role path for caching, since rp is mutable!
	// if(filler instanceof DomainConceptEQ<?,?>){
	// this.setCachedDomainConceptEQEntry(ind, myrp, (DomainConceptEQ<?, ?>)
	// filler);
	// }
	// }
	// if(this.cachedDomainConcepts == null){
	// this.cachedDomainConcepts = new HashMap<Individual, HashMap<RolePath,
	// HashMap<Role, String>>>();
	// }
	// HashMap<RolePath, HashMap<Role, String>> newsub;
	// if(this.cachedDomainConcepts.containsKey(ind)){
	// newsub = this.cachedDomainConcepts.get(ind);
	// }else{
	// newsub = new HashMap<RolePath, HashMap<Role, String>>();
	// }
	// setSubCachedDomainConceptEQEntry(newsub, myrp, dc);
	// this.cachedDomainConcepts.put(ind, newsub);
	// //System.err.println(displayCache());
	// }
	//
	// public String displayCache(){
	// String s="===content===\n";
	// for(Entry<Individual, HashMap<RolePath, HashMap<Role, String>>>
	// e:this.cachedDomainConcepts.entrySet()){
	// s += e.getKey() +": ";
	// for(Entry<RolePath, HashMap<Role, String>> ee:e.getValue().entrySet()){
	// s += "["+ee.getKey()+" -> "+ee.getValue()+"]";
	// }
	// s+="\n";
	// }
	// return s;
	// }
	//
	// public HashMap<Individual, HashMap<RolePath, HashMap<Role, String>>>
	// getCachedDCEntries(){
	// return this.cachedDomainConcepts;
	// }
	//
	// private void setSubCachedDomainConceptEQEntry(HashMap<RolePath,
	// HashMap<Role, String>> map,
	// RolePath rp, DomainConceptEQ<?,?> dc) throws CacheNodeException{
	// HashMap<Role, String> fkmap;
	// if(map.containsKey(rp)){
	// fkmap = map.get(rp);
	// }else{
	// fkmap = new HashMap<Role, String>();
	// }
	// if(fkmap.containsKey(dc.getRealFeature()) ){
	// if(dc.getRealValue().equals(fkmap.get(dc.getRealFeature()))){
	// if(rp.getOffset() == 0){
	// //no role path, f=1 and f=2 : error
	// throw new CacheNodeException("Value already exists for "+dc.toString());
	// }
	// //the same thing, not cached
	// return;
	// }
	//
	// }else{
	// fkmap.put((Role)dc.getRealFeature(), (String)dc.getRealValue());
	// map.put(rp, fkmap);
	// // System.err.println("Cache -- "
	// // +": "+rp.toString() +". "+dc.toString());
	// }
	// }
	//
	// public HashMap<Role, String> getCachedDomainConceptEQEntry(Individual
	// ind, RolePath rp){
	// if(this.cachedDomainConcepts == null){
	// return null;
	// }
	// if(this.cachedDomainConcepts.containsKey(ind)){
	// HashMap<RolePath, HashMap<Role, String>> sub =
	// this.cachedDomainConcepts.get(ind);
	// RolePath myrp;
	// myrp = new RolePath(null, rp.getOffset());
	// myrp.getRoles().addAll(rp.getRoles());
	// if(sub.containsKey(myrp)){
	// return sub.get(myrp);
	// }
	// }
	// return null;
	// }
	//
	// public DomainConceptEQ<?,?> getCachedDomainConceptEQEntry(Individual ind,
	// RolePath rp, Role f){
	// if(this.cachedDomainConcepts == null){
	// return null;
	// }
	// if(this.cachedDomainConcepts.containsKey(ind)){
	// HashMap<RolePath, HashMap<Role, String>> sub =
	// this.cachedDomainConcepts.get(ind);
	// RolePath myrp;
	// myrp = new RolePath(null, rp.getOffset());
	// myrp.getRoles().addAll(rp.getRoles());
	// if(sub.containsKey(myrp)){
	// HashMap<Role, String> dcs = sub.get(myrp);
	// // if(rp.getConcept() != null){
	// // if(! this.decideCacheSoundness(rp.getConcept(), dcs)){
	// // //not qualified to obtain the cache: otherwise unsound results
	// // return null;
	// // }
	// // }
	// if(dcs.containsKey(f)){
	// domainCacheHit ++;
	// return new DomainConceptEQ<Role,String>(f, dcs.get(f));
	// }
	// }
	// }
	// return null;
	// }

	/**
	 * Given a rp: Rp.C, looking for f?, we do the following. Analyze C, see if
	 * C is purely made of (f1=k1 AND f2=k2 AND ...) such that all the f_i = k_i
	 * are in the cache.
	 * 
	 * Note C is produced by Pd, i.e., C can only be of the form: C?, f?, Pd1
	 * AND Pd2, Pd (where Rp is changed by Exists.R) Later cache can further
	 * optimize such situations in case C is cached, instead of only f_i=k_i.
	 * 
	 * @return
	 */
//	public boolean decideCacheSoundness(Concept C, HashMap<Role, String> fks) {
//		boolean pure = false;
//		if (C == null) {
//			return true;
//		}
//		if (C instanceof DomainConceptEQ<?, ?>) {
//			DomainConceptEQ<?, ?> dc = (DomainConceptEQ<?, ?>) C;
//			if (fks.containsKey(dc.getRealFeature())) {
//				return true;
//			}
//		} else if (C instanceof AndConcept) {
//			for (Concept pc : ((AndConcept) C).getConceptSet()) {
//				pure &= this.decideCacheSoundness(pc, fks);
//				if (!pure)
//					return false;
//			}
//		}
//		return pure;
//	}

	public static DescriptionLogicsKB newNullKB() {
		return new DescriptionLogicsKB(null, null);
	}

	public static DescriptionLogicsKB newKBInstance(TBox tb, ABox ab) {
		return new DescriptionLogicsKB(tb, ab);
	}

	public static DescriptionLogicsKB newKBInstance(TBox tb, ABox ab,
			String name) {
		return new DescriptionLogicsKB(tb, ab, name);
	}

	private DescriptionLogicsKB(TBox tb, ABox ab) {
		this(tb, ab, KB_default_name);
	}

	private DescriptionLogicsKB(TBox tb, ABox ab, String name) {
		super();
		this.tb = tb;
		this.ab = ab;
		this.name = name;
		this.stat = new KBStat(this);
	}

	public String getKBName() {
		return this.name;
	}

	// public void setKBName(String name){
	// this.name = name;
	// }

	public TBox getTbox() {
		return tb;
	}

	public ABox getAbox() {
		return ab;
	}

	@Override
	public String toString() {
		return this.tb.toString() + "\n\n" + this.ab.toString();
	}

	public StringBuilder save(StringBuilder sb) {
		sb = this.tb.save(sb);
		sb = this.ab.save(sb);
		return sb;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof DescriptionLogicsKB))
			return false;
		DescriptionLogicsKB other = (DescriptionLogicsKB) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

}
